package cn.lijiajia3515.cairo.auth.framework.security.oauth2.server;

import cn.lijiajia3515.cairo.auth.domain.mongo.AccountMongo;
import cn.lijiajia3515.cairo.auth.domain.mongo.AuthMongoConstants;
import cn.lijiajia3515.cairo.auth.framework.AccountRepository;
import cn.lijiajia3515.cairo.auth.modules.oauth2.PasswordParam;
import cn.lijiajia3515.cairo.core.business.AuthBusiness;
import cn.lijiajia3515.cairo.core.exception.BusinessException;
import cn.lijiajia3515.cairo.domain.CairoAccount;
import cn.lijiajia3515.cairo.security.authentication.CairoAuthenticationToken;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Collection;
import java.util.Optional;

public class CairoOAuth2AuthorizationServerService {

	private final MongoTemplate mongoTemplate;

	/**
	 * 账号仓库
	 */
	protected final AccountRepository accountRepository;

	/**
	 * 密码 编码器
	 */
	private final PasswordEncoder passwordEncoder;

	public CairoOAuth2AuthorizationServerService(MongoTemplate mongoTemplate, AccountRepository accountRepository, PasswordEncoder passwordEncoder) {
		this.mongoTemplate = mongoTemplate;
		this.accountRepository = accountRepository;
		this.passwordEncoder = passwordEncoder;
	}

	public CairoAuthenticationToken authentication(PasswordParam param) {
		Query query = Query.query(
			new Criteria()
				.orOperator(
					Criteria.where(AccountMongo.FIELD.ID).is(param.getUsername()),
					Criteria.where(AccountMongo.FIELD.USERNAME).is(param.getUsername()),
					Criteria.where(AccountMongo.FIELD.PHONE_NUMBER).is(param.getUsername()),
					Criteria.where(AccountMongo.FIELD.EMAIL).is(param.getUsername())
				)
		).with(Sort.by(
			Sort.Order.asc(AccountMongo.FIELD.META.CREATED.AT),
			Sort.Order.asc(AccountMongo.FIELD.META.LAST_MODIFIED.AT),
			Sort.Order.asc(AccountMongo.FIELD._ID)
		));

		query.fields().include(AccountMongo.FIELD.ID, AccountMongo.FIELD.PASSWORD);

		AccountMongo am = Optional.ofNullable(mongoTemplate.findOne(query, AccountMongo.class, AuthMongoConstants.Collection.ACCOUNT))
			.orElseThrow(() -> new BusinessException(AuthBusiness.Bad));

		// 密码匹配
		if (!passwordEncoder.matches(param.getPassword(), am.getPassword())) {
			throw new BusinessException(AuthBusiness.Bad);
		}

		CairoAccount account = accountRepository.getAccount(param.getClientId(), am.getId());
		Collection<? extends GrantedAuthority> authorities = accountRepository.getAuthority(param.getClientId(), am.getId());
		return new CairoAuthenticationToken(account, authorities);
	}
}
